package com.components.update.http

import android.Manifest
import android.content.Context
import android.os.Handler
import android.os.Message
import android.util.Log
import com.blankj.utilcode.util.PermissionUtils
import com.components.update.FilePoint
import com.components.update.callback.DownloadListener
import com.components.update.util.AppUtils
import com.components.update.util.HttpUtil
import com.components.update.util.NotificationForProgressUtils
import okhttp3.Call
import okhttp3.Response
import java.io.Closeable
import java.io.File
import java.io.IOException
import java.io.RandomAccessFile
import java.util.concurrent.atomic.AtomicInteger

class DownloadTask internal constructor(
    private val context: Context,
    private val mPoint: FilePoint, //下载回调监听
    private val mListener: DownloadListener?
) : Handler() {
    private val THREAD_COUNT = 1 //线程数(这里目前给单线程，开多线程会出现EOF异常)
    private var mFileLength: Long = 0

    @Volatile
    var isDownloading = false
    private val childCancelCount = AtomicInteger(0) //子线程取消数量
    private val childPauseCount = AtomicInteger(0) //子线程暂停数量
    private val childFinishCount = AtomicInteger(0) //子线程完成数量
    private val mHttpUtil: HttpUtil
    private var mProgress: LongArray
    private val mCacheFiles: Array<File?>
    private var mTmpFile: File? = null//临时占位文件

    private var pause = false//是否暂停 = false
    private var cancel = false//是否取消下载 = false


    /**
     * 任务回调消息
     *
     * @param msg
     */
    override fun handleMessage(msg: Message) {
        if (null == mListener) {
            return
        }
        when (msg.what) {
            MSG_PROGRESS -> {
                var progress: Long = 0
                var i = 0
                val length = mProgress.size
                while (i < length) {
                    progress += mProgress[i]
                    i++
                }
                mListener.onProgress(progress * 1.0f / mFileLength)
            }
            MSG_PAUSE -> {
                if (confirmStatus(childPauseCount)) return
                resetStatus()
                mListener.onPause()
            }
            MSG_FINISH -> {
                val file: File = File(mPoint.filePath, mPoint.fileName)
                val s = context.packageName + ".fileProvider"
                if (mTmpFile == null) {
                    if (file.name.endsWith("apk")) {
                        AppUtils.installApk(context, file, s)
                    }
                    mListener.onFinished(File(mPoint.filePath, mPoint.fileName))
                    return
                }
                if (confirmStatus(childFinishCount)) return
                mTmpFile!!.renameTo(file) //下载完毕后，重命名目标文件名
                resetStatus()
                if (file.name.endsWith("apk")) {
                    AppUtils.installApk(context, file, s)
                }
                mListener.onFinished(file)
            }
            MSG_CANCEL -> {
                if (confirmStatus(childCancelCount)) return
                resetStatus()
                mProgress = LongArray(THREAD_COUNT)
                mListener.onCancel()
            }
        }
    }

    @Synchronized
    fun start(repeatDownLoad: Boolean = false) {//false为不需要重复下载
        try {
            if (isDownloading) return
            isDownloading = true
            if (File(mPoint.filePath, mPoint.fileName).exists() and !repeatDownLoad) {
                sendEmptyMessage(MSG_FINISH)
            } else {
                mHttpUtil.getContentLength(mPoint.url, object : okhttp3.Callback {
                    override fun onResponse(call: Call, response: Response) {
                        if (response.code != 200) {
                            close(response.body!!)
                            resetStatus()
                            return
                        }
                        // 获取资源大小
                        mFileLength = response.body!!.contentLength()
                        close(response.body!!)
                        // 在本地创建一个与资源同样大小的文件来占位
                        mTmpFile = File(mPoint.filePath, mPoint.fileName + ".tmp")
                        if (!mTmpFile!!.parentFile.exists()) mTmpFile!!.parentFile.mkdirs()
                        val tmpAccessFile = RandomAccessFile(mTmpFile!!, "rw")
                        tmpAccessFile.setLength(mFileLength)
                        /*将下载任务分配给每个线程*/
                        val blockSize = mFileLength / THREAD_COUNT // 计算每个线程理论上下载的数量.

                        /*为每个线程配置并分配任务*/
                        for (threadId in 0 until THREAD_COUNT) {
                            val startIndex = threadId * blockSize // 线程开始下载的位置
                            var endIndex = (threadId + 1) * blockSize - 1 // 线程结束下载的位置
                            if (threadId == THREAD_COUNT - 1) { // 如果是最后一个线程,将剩下的文件全部交给这个线程完成
                                endIndex = mFileLength - 1
                            }
                            download(startIndex, endIndex, threadId) // 开启线程下载
                        }
                    }

                    override fun onFailure(call: Call, e: IOException) {
                        resetStatus()
                    }
                })
            }
        } catch (e: IOException) {
            e.printStackTrace()
            resetStatus()
        }
    }

    private fun download(startIndex: Long, endIndex: Long, threadId: Int) {
        var newStartIndex = startIndex
        // 分段请求网络连接,分段将文件保存到本地.
        // 加载下载位置缓存文件
        val cacheFile = File(mPoint.filePath, "thread" + threadId + "_" + mPoint.fileName + ".cache")
        mCacheFiles[threadId] = cacheFile
        val cacheAccessFile = RandomAccessFile(cacheFile, "rwd")
        if (cacheFile.exists()) { // 如果文件存在
            val startIndexStr = cacheAccessFile.readLine()
            try {
                newStartIndex = startIndexStr.toLong() //重新设置下载起点
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        var finalStartIndex = newStartIndex
        if (finalStartIndex > endIndex) {
            cleanFile(cacheFile)
            finalStartIndex = 0
        }
        mHttpUtil.downloadFileByRange(
            mPoint.url,
            finalStartIndex,
            endIndex,
            object : okhttp3.Callback {
                override fun onResponse(call: Call, response: Response) {
                    if (response.code != 206) { // 206：请求部分资源成功码
                        resetStatus()
                        return
                    }
                    try {
                        val inputStream = response.body!!.byteStream() // 获取流
                        val tmpAccessFile = RandomAccessFile(mTmpFile, "rw") // 获取前面已创建的文件.
                        tmpAccessFile.seek(finalStartIndex) // 文件写入的开始位置.
                        /*  将网络流中的文件写入本地*/
                        val buffer = ByteArray(1024 shl 2)
                        var length = -1
                        var total = 0 // 记录本次下载文件的大小
                        var progress: Long = 0
                        while (inputStream.read(buffer).also { length = it } != -1) {
                            if (cancel) {
                                //关闭资源
                                close(cacheAccessFile, inputStream, response.body!!)
                                cleanFile(cacheFile)
                                sendEmptyMessage(MSG_CANCEL)
                                return
                            }
                            if (pause) {
                                //关闭资源
                                close(cacheAccessFile, inputStream, response.body!!)
                                //发送暂停消息
                                sendEmptyMessage(MSG_PAUSE)
                                return
                            }
                            tmpAccessFile.write(buffer, 0, length)
                            total += length
                            progress = finalStartIndex + total

                            //将当前现在到的位置保存到文件中
                            cacheAccessFile.seek(0)
                            cacheAccessFile.write((progress.toString() + "").toByteArray(charset("UTF-8")))
                            //发送进度消息
                            mProgress[threadId] = progress - startIndex
                            sendEmptyMessage(MSG_PROGRESS)
                        }
                        //关闭资源
                        close(cacheAccessFile, inputStream, response.body!!)
                        // 删除临时文件
                        cleanFile(cacheFile)
                        //发送完成消息
                        sendEmptyMessage(MSG_FINISH)
                    } catch (e: Exception) {
                        Log.e("lk", "onResponse: 抓住异常了$e")
                        mListener?.onCancel()
                    }

                }

                override fun onFailure(call: Call, e: IOException) {
                    isDownloading = false
                }
            })
    }

    /**
     * 关闭资源
     *
     * @param closeables
     */
    private fun close(vararg closeables: Closeable) {
        val length = closeables.size
        try {
            for (i in 0 until length) {
                val closeable = closeables[i]
                if (null != closeable) closeables[i].close()
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            for (i in 0 until length) {
                closeables[i].close()
            }
        }
    }

    /**
     * 删除临时文件
     */
    private fun cleanFile(vararg files: File?) {
        var i = 0
        val length = files.size
        while (i < length) {
            if (null != files[i]) files[i]!!.delete()
            i++
        }
    }

    /**
     * 暂停
     */
    fun pause() {
        pause = true
    }

    /**
     * 取消
     */
    fun cancel() {
        cancel = true
        cleanFile(mTmpFile)
        if (!isDownloading) {
            if (null != mListener) {
                cleanFile(*mCacheFiles)
                resetStatus()
                mListener.onCancel()
            }
        }
    }

    /**
     * 重置下载状态
     */
    private fun resetStatus() {
        pause = false
        cancel = false
        isDownloading = false
    }

    /**
     * 确认下载状态
     *
     * @param count
     * @return
     */
    private fun confirmStatus(count: AtomicInteger): Boolean {
        return count.incrementAndGet() % THREAD_COUNT != 0
    }

    companion object {
        private const val MSG_PROGRESS = 1 //进度
        private const val MSG_FINISH = 2 //完成下载
        private const val MSG_PAUSE = 3 //暂停
        private const val MSG_CANCEL = 4 //取消
        private const val TAG = "DownloadTask"
    }

    /**
     * 任务管理器初始化数据
     *
     * @param point
     * @param l
     */
    init {
        mProgress = LongArray(THREAD_COUNT)
        mCacheFiles = arrayOfNulls(THREAD_COUNT)
        mHttpUtil = HttpUtil.instance!!
    }
}